﻿using System;
using System.Collections.Generic;
using System.Text;
using AdaptiveAgents.Agents;
using AdaptiveAgents.Games;
using AdaptiveAgents.Distributions;
using AdaptiveAgents.Loggers;
using System.Data;
using AdaptiveAgents.Results;

namespace AdaptiveAgents.Experiments
{
    /// <summary>
    /// The basic experiment to find out what is the most efficient epsilon to use.
    /// </summary>
    class FindBestEpsilonExperiment2 : Experiment
    {
        public FindBestEpsilonExperiment2() : base(50, 3, 1) { }
        private ExperimentResults results;

        private DataTable dtNormal;//A DataTable for a normal run

        /// <summary>
        /// A function to write the XML file as a CSV file ready for opening in excel
        /// </summary>
        /// <param name="fileName">Name of the file</param>
        /// <param name="dt">The data Table to print</param>
        private void WriteToCSV(String fileName, DataTable dt)
        {
            //set an output writer
            System.IO.TextWriter writer = new System.IO.StreamWriter(fileName);

            //print columns name
            for (int i = 0; i < dt.Columns.Count; ++i)
                writer.Write(dt.Columns[i].ToString() + ",");

            writer.WriteLine();

            //for each row...
            for (int i = 0; i < dt.Rows.Count; ++i)
            {
                DataRow row = dt.Rows[i];
                //for each cell in the row...
                for (int j = 0; j < dt.Columns.Count; ++j)
                    writer.Write(row[j].ToString() + ",");
                writer.WriteLine();
            }

            writer.Close();
        }

        /// <summary>
        /// Run the main experiment
        /// </summary>
        /// <param name="eps">Insert epsilon to be tested</param>
        public override void runExperiment()
        {
            //Set the competence level, the higher it is - the better the player is.
            double competence0;
            double competence1;
            double competence2;

            double eps0;
            double eps1;
            double eps2;

            int counter;
            results = new ExperimentResults();
            for (counter = 0; counter < 1; counter++)
            {
                //Console.WriteLine("===== New game with epsilon " + epss);
                
                int GAMES = 20; //number of games to run
                int ROUNDS = 1000; //number of round each game
                DataRow row1; //a row for each type of agent

                double utility = 0;

                dtNormal = new DataTable("Normal_Data_Table");
                DataColumn cln = new DataColumn("GameNum", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Competence0", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Competence1", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Competence2", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("eps0", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("eps1", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("eps2", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("avgUtilityOfTheGame", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Player0NumOfTurnsPlayed", typeof(int));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Player1NumOfTurnsPlayed", typeof(int));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Player2NumOfTurnsPlayed", typeof(int));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Player0AccumUtility", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Player1AccumUtility", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Player2AccumUtility", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Player0AvgUtility", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Player1AvgUtility", typeof(double));
                dtNormal.Columns.Add(cln);
                cln = new DataColumn("Player2AvgUtility", typeof(double));
                dtNormal.Columns.Add(cln);


                //add rows for each round
                for (int i = 0; i < ROUNDS; ++i)
                {
                    cln = new DataColumn(i.ToString(), typeof(double));
                    dtNormal.Columns.Add("utility " + cln);
                }

                //run games
                for (int i = 0; i < GAMES; ++i)
                {
                    //Get random starting values for the epsilon and competence:
                    Random rand = new Random();
                    eps0 = Math.Round(rand.NextDouble(), 1);
                    CheckNumIsBetweenValidRange(ref eps0);
                    do
                    {
                        eps1 = Math.Round(rand.NextDouble(), 1);
                        CheckNumIsBetweenValidRange(ref eps1);
                    } while (eps1 == eps0);
                    
                    do
                    {
                        eps2 = Math.Round(rand.NextDouble(), 1);
                        CheckNumIsBetweenValidRange(ref eps2);
                    } while (eps2 == eps1 || eps2 == eps0);                    

                    competence0 = Math.Round(rand.NextDouble(), 1);                   
                    CheckNumIsBetweenValidRange(ref competence0);
                    do
                    {
                        competence1 = Math.Round(rand.NextDouble(), 1);
                        CheckNumIsBetweenValidRange(ref competence1);
                    } while (competence1 == competence0);
                    do
                    {
                        competence2 = Math.Round(rand.NextDouble(), 1);
                        CheckNumIsBetweenValidRange(ref competence2);
                    } while (competence2 == competence1 || competence2 == competence0);
                    
                    
                    //write to console to see progress
                    if (i % 100 == 0)
                        System.Console.WriteLine(i);

                    //set new rows for the game
                    row1 = dtNormal.NewRow();

                    //enter game number
                    row1["GameNum"] = i;

                    //save the epsilon value in the DataRows
                    row1["eps0"] = eps0;
                    row1["eps1"] = eps1;
                    row1["eps2"] = eps2;

                    //save the competence value in the DataRows
                    row1["competence0"] = competence0;
                    row1["competence1"] = competence1;
                    row1["competence2"] = competence2;

                    //Create a new environment for the experiment
                    Environment environment = new Environment();

                    //Create a list of 3 agents
                    List<Agent> agents = generatePlayers(environment, eps0, eps1, eps2, AdaptiveAgents.logger);

                    //Add agents to the environment
                    for (int j = 0; j < NumAgents; j++)
                    {
                        Agent agent = agents[j];
                        environment.addAgent(agent);
                    }

                    //Create a new normal game
                    //NormalGame game = new NormalGame(environment, 10);
                    NormalGameToCSV game = (NormalGameToCSV)GameFactory.create(Games.GameType.NormalToCsv, environment, ROUNDS);

                    //Set the competence level for each agent
                    environment.agents[0].Competence = new Blocks(competence0);
                    environment.agents[1].Competence = new Blocks(competence1);
                    environment.agents[2].Competence = new Blocks(competence2);

                    //enter to the game the row it needs to write the game data to
                    game.Dr1 = row1;

                    //Start the game
                    utility = game.start(AdaptiveAgents.logger, out results);

                    row1["avgUtilityOfTheGame"] = utility;
                    row1["Player0NumOfTurnsPlayed"] = results.numberOfTurnsPlayer0Played;
                    row1["Player1NumOfTurnsPlayed"] = results.numberOfTurnsPlayer1Played;
                    row1["Player2NumOfTurnsPlayed"] = results.numberOfTurnsPlayer2Played;

                    row1["Player0AccumUtility"] = results.player0AccumilatingProfit;
                    row1["Player1AccumUtility"] = results.player1AccumilatingProfit;
                    row1["Player2AccumUtility"] = results.player2AccumilatingProfit;

                    row1["Player0AvgUtility"] = results.player0AccumilatingProfit / results.numberOfTurnsPlayer0Played;
                    row1["Player1AvgUtility"] = results.player1AccumilatingProfit / results.numberOfTurnsPlayer1Played;
                    row1["Player2AvgUtility"] = results.player2AccumilatingProfit / results.numberOfTurnsPlayer2Played;

                    dtNormal.Rows.Add(row1);

                    //Print to screen current utility if needed (cancel to increase speed)
                    //System.Console.WriteLine(utility);
                }
                //create file names and write to the files
                String stamp = DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss");
                //dtNormal.WriteXml("FindBestEpsilonExperiment_" + stamp + ".csv");

                WriteToCSV("FindBestEpsilonExperiment_" + counter + "_" + stamp + ".csv", dtNormal);
                
            }
        }
    }
}
